/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Haystack Software Inc. All rights reserved.
 *  Licensed under the PolyForm Strict License 1.0.0. See License.txt in the project root for
 *  license information.
 *--------------------------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See code-license.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as DOM from "vs/base/browser/dom"
import { StandardKeyboardEvent } from "vs/base/browser/keyboardEvent"
import { HoverPosition } from "vs/base/browser/ui/hover/hoverWidget"
import { SimpleIconLabel } from "vs/base/browser/ui/iconLabel/simpleIconLabel"
import { RunOnceScheduler } from "vs/base/common/async"
import { Emitter } from "vs/base/common/event"
import { IMarkdownString } from "vs/base/common/htmlContent"
import { KeyCode } from "vs/base/common/keyCodes"
import { IDisposable, DisposableStore } from "vs/base/common/lifecycle"
import { ILanguageService } from "vs/editor/common/languages/language"
import { localize } from "vs/nls"
import { ICommandService } from "vs/platform/commands/common/commands"
import { ConfigurationTarget } from "vs/platform/configuration/common/configuration"
import { IUserDataProfilesService } from "vs/platform/userDataProfile/common/userDataProfile"
import { IUserDataSyncEnablementService } from "vs/platform/userDataSync/common/userDataSync"
import { SettingsTreeSettingElement } from "vs/workbench/contrib/preferences/browser/settingsTreeModels"
import { POLICY_SETTING_TAG } from "vs/workbench/contrib/preferences/common/preferences"
import { IWorkbenchConfigurationService } from "vs/workbench/services/configuration/common/configuration"
import { IHoverService } from "vs/platform/hover/browser/hover"
import type {
  IHoverOptions,
  IHoverWidget,
} from "vs/base/browser/ui/hover/hover"

const $ = DOM.$

type ScopeString = "workspace" | "user" | "remote" | "default"

export interface ISettingOverrideClickEvent {
  scope: ScopeString
  language: string
  settingKey: string
}

interface SettingIndicator {
  element: HTMLElement
  /**
   * The element to focus on when navigating with keyboard.
   * When undefined, use {@link element} instead.
   */
  focusElement?: HTMLElement
  label: SimpleIconLabel
  disposables: DisposableStore
}

/**
 * Contains a set of the sync-ignored settings
 * to keep the sync ignored indicator and the getIndicatorsLabelAriaLabel() function in sync.
 * SettingsTreeIndicatorsLabel#updateSyncIgnored provides the source of truth.
 */
let cachedSyncIgnoredSettingsSet: Set<string> = new Set<string>()

/**
 * Contains a copy of the sync-ignored settings to determine when to update
 * cachedSyncIgnoredSettingsSet.
 */
let cachedSyncIgnoredSettings: string[] = []

/**
 * Renders the indicators next to a setting, such as "Also Modified In".
 */
export class SettingsTreeIndicatorsLabel implements IDisposable {
  private readonly indicatorsContainerElement: HTMLElement

  private readonly workspaceTrustIndicator: SettingIndicator
  private readonly scopeOverridesIndicator: SettingIndicator
  private readonly syncIgnoredIndicator: SettingIndicator
  private readonly defaultOverrideIndicator: SettingIndicator
  private readonly allIndicators: SettingIndicator[]

  private readonly profilesEnabled: boolean

  private readonly keybindingListeners: DisposableStore = new DisposableStore()
  private focusedIndex = 0

  constructor(
    container: HTMLElement,
    @IWorkbenchConfigurationService
    private readonly configurationService: IWorkbenchConfigurationService,
    @IHoverService private readonly hoverService: IHoverService,
    @IUserDataSyncEnablementService
    private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
    @ILanguageService private readonly languageService: ILanguageService,
    @IUserDataProfilesService
    private readonly userDataProfilesService: IUserDataProfilesService,
    @ICommandService private readonly commandService: ICommandService
  ) {
    this.indicatorsContainerElement = DOM.append(
      container,
      $(".setting-indicators-container")
    )
    this.indicatorsContainerElement.style.display = "inline"

    this.profilesEnabled = this.userDataProfilesService.isEnabled()

    this.workspaceTrustIndicator = this.createWorkspaceTrustIndicator()
    this.scopeOverridesIndicator = this.createScopeOverridesIndicator()
    this.syncIgnoredIndicator = this.createSyncIgnoredIndicator()
    this.defaultOverrideIndicator = this.createDefaultOverrideIndicator()
    this.allIndicators = [
      this.workspaceTrustIndicator,
      this.scopeOverridesIndicator,
      this.syncIgnoredIndicator,
      this.defaultOverrideIndicator,
    ]
  }

  private defaultHoverOptions: Partial<IHoverOptions> = {
    trapFocus: true,
    position: {
      hoverPosition: HoverPosition.BELOW,
    },
    appearance: {
      showPointer: true,
      compact: false,
    },
  }

  private addHoverDisposables(
    disposables: DisposableStore,
    element: HTMLElement,
    showHover: (focus: boolean) => IHoverWidget | undefined
  ) {
    disposables.clear()
    const scheduler: RunOnceScheduler = disposables.add(
      new RunOnceScheduler(() => {
        const hover = showHover(false)
        if (hover) {
          disposables.add(hover)
        }
      }, this.configurationService.getValue<number>("workbench.hover.delay"))
    )
    disposables.add(
      DOM.addDisposableListener(element, DOM.EventType.MOUSE_OVER, () => {
        if (!scheduler.isScheduled()) {
          scheduler.schedule()
        }
      })
    )
    disposables.add(
      DOM.addDisposableListener(element, DOM.EventType.MOUSE_LEAVE, () => {
        scheduler.cancel()
      })
    )
    disposables.add(
      DOM.addDisposableListener(element, DOM.EventType.KEY_DOWN, (e) => {
        const evt = new StandardKeyboardEvent(e)
        if (evt.equals(KeyCode.Space) || evt.equals(KeyCode.Enter)) {
          const hover = showHover(true)
          if (hover) {
            disposables.add(hover)
          }
          e.preventDefault()
        }
      })
    )
  }

  private createWorkspaceTrustIndicator(): SettingIndicator {
    const disposables = new DisposableStore()
    const workspaceTrustElement = $(
      "span.setting-indicator.setting-item-workspace-trust"
    )
    const workspaceTrustLabel = disposables.add(
      new SimpleIconLabel(workspaceTrustElement)
    )
    workspaceTrustLabel.text =
      "$(warning) " +
      localize("workspaceUntrustedLabel", "Setting value not applied")

    const content = localize(
      "trustLabel",
      "The setting value can only be applied in a trusted workspace."
    )
    const showHover = (focus: boolean) => {
      return this.hoverService.showHover(
        {
          ...this.defaultHoverOptions,
          content,
          target: workspaceTrustElement,
          actions: [
            {
              label: localize("manageWorkspaceTrust", "Manage Workspace Trust"),
              commandId: "workbench.trust.manage",
              run: (target: HTMLElement) => {
                this.commandService.executeCommand("workbench.trust.manage")
              },
            },
          ],
        },
        focus
      )
    }
    this.addHoverDisposables(disposables, workspaceTrustElement, showHover)
    return {
      element: workspaceTrustElement,
      label: workspaceTrustLabel,
      disposables,
    }
  }

  private createScopeOverridesIndicator(): SettingIndicator {
    const disposables = new DisposableStore()
    // Don't add .setting-indicator class here, because it gets conditionally added later.
    const otherOverridesElement = $("span.setting-item-overrides")
    const otherOverridesLabel = disposables.add(
      new SimpleIconLabel(otherOverridesElement)
    )
    return {
      element: otherOverridesElement,
      label: otherOverridesLabel,
      disposables,
    }
  }

  private createSyncIgnoredIndicator(): SettingIndicator {
    const disposables = new DisposableStore()
    const syncIgnoredElement = $("span.setting-indicator.setting-item-ignored")
    const syncIgnoredLabel = disposables.add(
      new SimpleIconLabel(syncIgnoredElement)
    )
    syncIgnoredLabel.text = localize("extensionSyncIgnoredLabel", "Not synced")

    const syncIgnoredHoverContent = localize(
      "syncIgnoredTitle",
      "This setting is ignored during sync"
    )
    const showHover = (focus: boolean) => {
      return this.hoverService.showHover(
        {
          ...this.defaultHoverOptions,
          content: syncIgnoredHoverContent,
          target: syncIgnoredElement,
        },
        focus
      )
    }
    this.addHoverDisposables(disposables, syncIgnoredElement, showHover)

    return {
      element: syncIgnoredElement,
      label: syncIgnoredLabel,
      disposables,
    }
  }

  private createDefaultOverrideIndicator(): SettingIndicator {
    const disposables = new DisposableStore()
    const defaultOverrideIndicator = $(
      "span.setting-indicator.setting-item-default-overridden"
    )
    const defaultOverrideLabel = disposables.add(
      new SimpleIconLabel(defaultOverrideIndicator)
    )
    defaultOverrideLabel.text = localize(
      "defaultOverriddenLabel",
      "Default value changed"
    )

    return {
      element: defaultOverrideIndicator,
      label: defaultOverrideLabel,
      disposables,
    }
  }

  private render() {
    const indicatorsToShow = this.allIndicators.filter((indicator) => {
      return indicator.element.style.display !== "none"
    })

    this.indicatorsContainerElement.innerText = ""
    this.indicatorsContainerElement.style.display = "none"
    if (indicatorsToShow.length) {
      this.indicatorsContainerElement.style.display = "inline"
      DOM.append(this.indicatorsContainerElement, $("span", undefined, "("))
      for (let i = 0; i < indicatorsToShow.length - 1; i++) {
        DOM.append(this.indicatorsContainerElement, indicatorsToShow[i].element)
        DOM.append(
          this.indicatorsContainerElement,
          $("span.comma", undefined, " • ")
        )
      }
      DOM.append(
        this.indicatorsContainerElement,
        indicatorsToShow[indicatorsToShow.length - 1].element
      )
      DOM.append(this.indicatorsContainerElement, $("span", undefined, ")"))
      this.resetIndicatorNavigationKeyBindings(indicatorsToShow)
    }
  }

  private resetIndicatorNavigationKeyBindings(indicators: SettingIndicator[]) {
    this.keybindingListeners.clear()
    this.indicatorsContainerElement.role =
      indicators.length >= 1 ? "toolbar" : "button"
    if (!indicators.length) {
      return
    }
    const firstElement = indicators[0].focusElement ?? indicators[0].element
    firstElement.tabIndex = 0
    this.keybindingListeners.add(
      DOM.addDisposableListener(
        this.indicatorsContainerElement,
        "keydown",
        (e) => {
          const ev = new StandardKeyboardEvent(e)
          let handled = true
          if (ev.equals(KeyCode.Home)) {
            this.focusIndicatorAt(indicators, 0)
          } else if (ev.equals(KeyCode.End)) {
            this.focusIndicatorAt(indicators, indicators.length - 1)
          } else if (ev.equals(KeyCode.RightArrow)) {
            const indexToFocus = (this.focusedIndex + 1) % indicators.length
            this.focusIndicatorAt(indicators, indexToFocus)
          } else if (ev.equals(KeyCode.LeftArrow)) {
            const indexToFocus = this.focusedIndex
              ? this.focusedIndex - 1
              : indicators.length - 1
            this.focusIndicatorAt(indicators, indexToFocus)
          } else {
            handled = false
          }

          if (handled) {
            e.preventDefault()
            e.stopPropagation()
          }
        }
      )
    )
  }

  private focusIndicatorAt(indicators: SettingIndicator[], index: number) {
    if (index === this.focusedIndex) {
      return
    }
    const indicator = indicators[index]
    const elementToFocus = indicator.focusElement ?? indicator.element
    elementToFocus.tabIndex = 0
    elementToFocus.focus()

    const currentlyFocusedIndicator = indicators[this.focusedIndex]
    const previousFocusedElement =
      currentlyFocusedIndicator.focusElement ??
      currentlyFocusedIndicator.element
    previousFocusedElement.tabIndex = -1

    this.focusedIndex = index
  }

  updateWorkspaceTrust(element: SettingsTreeSettingElement) {
    this.workspaceTrustIndicator.element.style.display = element.isUntrusted
      ? "inline"
      : "none"
    this.render()
  }

  updateSyncIgnored(
    element: SettingsTreeSettingElement,
    ignoredSettings: string[]
  ) {
    this.syncIgnoredIndicator.element.style.display =
      this.userDataSyncEnablementService.isEnabled() &&
      ignoredSettings.includes(element.setting.key)
        ? "inline"
        : "none"
    this.render()
    if (cachedSyncIgnoredSettings !== ignoredSettings) {
      cachedSyncIgnoredSettings = ignoredSettings
      cachedSyncIgnoredSettingsSet = new Set<string>(cachedSyncIgnoredSettings)
    }
  }

  private getInlineScopeDisplayText(completeScope: string): string {
    const [scope, language] = completeScope.split(":")
    const localizedScope =
      scope === "user"
        ? localize("user", "User")
        : scope === "workspace"
        ? localize("workspace", "Workspace")
        : localize("remote", "Remote")
    if (language) {
      return `${this.languageService.getLanguageName(
        language
      )} > ${localizedScope}`
    }
    return localizedScope
  }

  dispose() {
    this.keybindingListeners.dispose()
    for (const indicator of this.allIndicators) {
      indicator.disposables.dispose()
    }
  }

  updateScopeOverrides(
    element: SettingsTreeSettingElement,
    onDidClickOverrideElement: Emitter<ISettingOverrideClickEvent>,
    onApplyFilter: Emitter<string>
  ) {
    this.scopeOverridesIndicator.element.innerText = ""
    this.scopeOverridesIndicator.element.style.display = "none"
    this.scopeOverridesIndicator.focusElement =
      this.scopeOverridesIndicator.element
    if (element.hasPolicyValue) {
      // If the setting falls under a policy, then no matter what the user sets, the policy value takes effect.
      this.scopeOverridesIndicator.element.style.display = "inline"
      this.scopeOverridesIndicator.element.classList.add("setting-indicator")

      this.scopeOverridesIndicator.label.text =
        "$(warning) " + localize("policyLabelText", "Setting value not applied")
      const content = localize(
        "policyDescription",
        "This setting is managed by your organization and its applied value cannot be changed."
      )
      const showHover = (focus: boolean) => {
        return this.hoverService.showHover(
          {
            ...this.defaultHoverOptions,
            content,
            actions: [
              {
                label: localize("policyFilterLink", "View policy settings"),
                commandId: "_settings.action.viewPolicySettings",
                run: (_) => {
                  onApplyFilter.fire(`@${POLICY_SETTING_TAG}`)
                },
              },
            ],
            target: this.scopeOverridesIndicator.element,
          },
          focus
        )
      }
      this.addHoverDisposables(
        this.scopeOverridesIndicator.disposables,
        this.scopeOverridesIndicator.element,
        showHover
      )
    } else if (
      this.profilesEnabled &&
      element.settingsTarget === ConfigurationTarget.USER_LOCAL &&
      this.configurationService.isSettingAppliedForAllProfiles(
        element.setting.key
      )
    ) {
      this.scopeOverridesIndicator.element.style.display = "inline"
      this.scopeOverridesIndicator.element.classList.add("setting-indicator")

      this.scopeOverridesIndicator.label.text = localize(
        "applicationSetting",
        "Applies to all profiles"
      )

      const content = localize(
        "applicationSettingDescription",
        "The setting is not specific to the current profile, and will retain its value when switching profiles."
      )
      const showHover = (focus: boolean) => {
        return this.hoverService.showHover(
          {
            ...this.defaultHoverOptions,
            content,
            target: this.scopeOverridesIndicator.element,
          },
          focus
        )
      }
      this.addHoverDisposables(
        this.scopeOverridesIndicator.disposables,
        this.scopeOverridesIndicator.element,
        showHover
      )
    } else if (
      element.overriddenScopeList.length ||
      element.overriddenDefaultsLanguageList.length
    ) {
      if (
        element.overriddenScopeList.length === 1 &&
        !element.overriddenDefaultsLanguageList.length
      ) {
        // We can inline the override and show all the text in the label
        // so that users don't have to wait for the hover to load
        // just to click into the one override there is.
        this.scopeOverridesIndicator.element.style.display = "inline"
        this.scopeOverridesIndicator.element.classList.remove(
          "setting-indicator"
        )
        this.scopeOverridesIndicator.disposables.clear()

        const prefaceText = element.isConfigured
          ? localize("alsoConfiguredIn", "Also modified in")
          : localize("configuredIn", "Modified in")
        this.scopeOverridesIndicator.label.text = `${prefaceText} `

        const overriddenScope = element.overriddenScopeList[0]
        const view = DOM.append(
          this.scopeOverridesIndicator.element,
          $(
            "a.modified-scope",
            undefined,
            this.getInlineScopeDisplayText(overriddenScope)
          )
        )
        view.tabIndex = -1
        this.scopeOverridesIndicator.focusElement = view
        const onClickOrKeydown = (e: UIEvent) => {
          const [scope, language] = overriddenScope.split(":")
          onDidClickOverrideElement.fire({
            settingKey: element.setting.key,
            scope: scope as ScopeString,
            language,
          })
          e.preventDefault()
          e.stopPropagation()
        }
        this.scopeOverridesIndicator.disposables.add(
          DOM.addDisposableListener(view, DOM.EventType.CLICK, (e) => {
            onClickOrKeydown(e)
          })
        )
        this.scopeOverridesIndicator.disposables.add(
          DOM.addDisposableListener(view, DOM.EventType.KEY_DOWN, (e) => {
            const ev = new StandardKeyboardEvent(e)
            if (ev.equals(KeyCode.Space) || ev.equals(KeyCode.Enter)) {
              onClickOrKeydown(e)
            }
          })
        )
      } else {
        this.scopeOverridesIndicator.element.style.display = "inline"
        this.scopeOverridesIndicator.element.classList.add("setting-indicator")
        const scopeOverridesLabelText = element.isConfigured
          ? localize("alsoConfiguredElsewhere", "Also modified elsewhere")
          : localize("configuredElsewhere", "Modified elsewhere")
        this.scopeOverridesIndicator.label.text = scopeOverridesLabelText

        let contentMarkdownString = ""
        if (element.overriddenScopeList.length) {
          const prefaceText = element.isConfigured
            ? localize(
                "alsoModifiedInScopes",
                "The setting has also been modified in the following scopes:"
              )
            : localize(
                "modifiedInScopes",
                "The setting has been modified in the following scopes:"
              )
          contentMarkdownString = prefaceText
          for (const scope of element.overriddenScopeList) {
            const scopeDisplayText = this.getInlineScopeDisplayText(scope)
            contentMarkdownString += `\n- [${scopeDisplayText}](${encodeURIComponent(
              scope
            )} "${getAccessibleScopeDisplayText(scope, this.languageService)}")`
          }
        }
        if (element.overriddenDefaultsLanguageList.length) {
          if (contentMarkdownString) {
            contentMarkdownString += `\n\n`
          }
          const prefaceText = localize(
            "hasDefaultOverridesForLanguages",
            "The following languages have default overrides:"
          )
          contentMarkdownString += prefaceText
          for (const language of element.overriddenDefaultsLanguageList) {
            const scopeDisplayText =
              this.languageService.getLanguageName(language)
            contentMarkdownString += `\n- [${scopeDisplayText}](${encodeURIComponent(
              `default:${language}`
            )} "${scopeDisplayText}")`
          }
        }
        const content: IMarkdownString = {
          value: contentMarkdownString,
          isTrusted: false,
          supportHtml: false,
        }
        const showHover = (focus: boolean) => {
          return this.hoverService.showHover(
            {
              ...this.defaultHoverOptions,
              content,
              linkHandler: (url: string) => {
                const [scope, language] = decodeURIComponent(url).split(":")
                onDidClickOverrideElement.fire({
                  settingKey: element.setting.key,
                  scope: scope as ScopeString,
                  language,
                })
              },
              target: this.scopeOverridesIndicator.element,
            },
            focus
          )
        }
        this.addHoverDisposables(
          this.scopeOverridesIndicator.disposables,
          this.scopeOverridesIndicator.element,
          showHover
        )
      }
    }
    this.render()
  }

  updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) {
    this.defaultOverrideIndicator.element.style.display = "none"
    const sourceToDisplay = getDefaultValueSourceToDisplay(element)
    if (sourceToDisplay !== undefined) {
      this.defaultOverrideIndicator.element.style.display = "inline"
      this.defaultOverrideIndicator.disposables.clear()

      const defaultOverrideHoverContent = localize(
        "defaultOverriddenDetails",
        "Default setting value overridden by {0}",
        sourceToDisplay
      )
      const showHover = (focus: boolean) => {
        return this.hoverService.showHover(
          {
            content: defaultOverrideHoverContent,
            target: this.defaultOverrideIndicator.element,
            position: {
              hoverPosition: HoverPosition.BELOW,
            },
            appearance: {
              showPointer: true,
              compact: false,
            },
          },
          focus
        )
      }
      this.addHoverDisposables(
        this.defaultOverrideIndicator.disposables,
        this.defaultOverrideIndicator.element,
        showHover
      )
    }
    this.render()
  }
}

function getDefaultValueSourceToDisplay(
  element: SettingsTreeSettingElement
): string | undefined {
  let sourceToDisplay: string | undefined
  const defaultValueSource = element.defaultValueSource
  if (defaultValueSource) {
    if (typeof defaultValueSource !== "string") {
      sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id
    } else if (typeof defaultValueSource === "string") {
      sourceToDisplay = defaultValueSource
    }
  }
  return sourceToDisplay
}

function getAccessibleScopeDisplayText(
  completeScope: string,
  languageService: ILanguageService
): string {
  const [scope, language] = completeScope.split(":")
  const localizedScope =
    scope === "user"
      ? localize("user", "User")
      : scope === "workspace"
      ? localize("workspace", "Workspace")
      : localize("remote", "Remote")
  if (language) {
    return localize(
      "modifiedInScopeForLanguage",
      "The {0} scope for {1}",
      localizedScope,
      languageService.getLanguageName(language)
    )
  }
  return localizedScope
}

function getAccessibleScopeDisplayMidSentenceText(
  completeScope: string,
  languageService: ILanguageService
): string {
  const [scope, language] = completeScope.split(":")
  const localizedScope =
    scope === "user"
      ? localize("user", "User")
      : scope === "workspace"
      ? localize("workspace", "Workspace")
      : localize("remote", "Remote")
  if (language) {
    return localize(
      "modifiedInScopeForLanguageMidSentence",
      "the {0} scope for {1}",
      localizedScope.toLowerCase(),
      languageService.getLanguageName(language)
    )
  }
  return localizedScope
}

export function getIndicatorsLabelAriaLabel(
  element: SettingsTreeSettingElement,
  configurationService: IWorkbenchConfigurationService,
  userDataProfilesService: IUserDataProfilesService,
  languageService: ILanguageService
): string {
  const ariaLabelSections: string[] = []

  // Add workspace trust text
  if (element.isUntrusted) {
    ariaLabelSections.push(
      localize(
        "workspaceUntrustedAriaLabel",
        "Workspace untrusted; setting value not applied"
      )
    )
  }

  if (element.hasPolicyValue) {
    ariaLabelSections.push(
      localize(
        "policyDescriptionAccessible",
        "Managed by organization policy; setting value not applied"
      )
    )
  } else if (
    userDataProfilesService.isEnabled() &&
    element.settingsTarget === ConfigurationTarget.USER_LOCAL &&
    configurationService.isSettingAppliedForAllProfiles(element.setting.key)
  ) {
    ariaLabelSections.push(
      localize(
        "applicationSettingDescriptionAccessible",
        "Setting value retained when switching profiles"
      )
    )
  } else {
    // Add other overrides text
    const otherOverridesStart = element.isConfigured
      ? localize("alsoConfiguredIn", "Also modified in")
      : localize("configuredIn", "Modified in")
    const otherOverridesList = element.overriddenScopeList
      .map((scope) =>
        getAccessibleScopeDisplayMidSentenceText(scope, languageService)
      )
      .join(", ")
    if (element.overriddenScopeList.length) {
      ariaLabelSections.push(`${otherOverridesStart} ${otherOverridesList}`)
    }
  }

  // Add sync ignored text
  if (cachedSyncIgnoredSettingsSet.has(element.setting.key)) {
    ariaLabelSections.push(
      localize("syncIgnoredAriaLabel", "Setting ignored during sync")
    )
  }

  // Add default override indicator text
  const sourceToDisplay = getDefaultValueSourceToDisplay(element)
  if (sourceToDisplay !== undefined) {
    ariaLabelSections.push(
      localize(
        "defaultOverriddenDetailsAriaLabel",
        "{0} overrides the default value",
        sourceToDisplay
      )
    )
  }

  // Add text about default values being overridden in other languages
  const otherLanguageOverridesList = element.overriddenDefaultsLanguageList
    .map((language) => languageService.getLanguageName(language))
    .join(", ")
  if (element.overriddenDefaultsLanguageList.length) {
    const otherLanguageOverridesText = localize(
      "defaultOverriddenLanguagesList",
      "Language-specific default values exist for {0}",
      otherLanguageOverridesList
    )
    ariaLabelSections.push(otherLanguageOverridesText)
  }

  const ariaLabel = ariaLabelSections.join(". ")
  return ariaLabel
}
